#include <FL/Fl.H>
#include <FL/fl_ask.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include "heroz.hh"
#include "heroz_screens.hh"

SDL_Surface *cursor18;
HeroZ *heroz;
int quit = 0;
int screen = 0;
int crsrx = 0;
int crsry = 0;
int tilebank = 2;

static const char keychars[] = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ12345";

/* XPM */
static const char * cursor_18_xpm[] = {
"72 18 4 1",
" 	c None",
":	c #0a0a0a",
"+	c #008200",
"@	c #00FF00",
"@ @ @ @ @ @ @ @ @ :::::::::::::::::: @ @ @ @ @ @ @ @ @++++++++++++++++++",
"                 @:                :@                 +                +",
"@                 :                :                 @+                +",
"                 @:                :@                 +                +",
"@                 :                :                 @+                +",
"                 @:                :@                 +                +",
"@                 :                :                 @+                +",
"                 @:                :@                 +                +",
"@                 :                :                 @+                +",
"                 @:                :@                 +                +",
"@                 :                :                 @+                +",
"                 @:                :@                 +                +",
"@                 :                :                 @+                +",
"                 @:                :@                 +                +",
"@                 :                :                 @+                +",
"                 @:                :@                 +                +",
"@                 :                :                 @+                +",
" @ @ @ @ @ @ @ @ @::::::::::::::::::@ @ @ @ @ @ @ @ @ ++++++++++++++++++"};

struct CLIArgs {
  int argc;
  char **argv;
};

static ScreenCharacters copy_and_paste_buffer;
 
int displaythread(void *ptr) {
  Uint32 col;
  SDL_Rect src, dest;
  char buf[101];
  int i;
  SDL_Surface *scr = (SDL_Surface *)ptr;
  Uint32 frame = 0;

  do {
    col = SDL_MapRGB(scr->format, 0, 0x11, 0x3a);
    SDL_FillRect(scr, NULL, col);
    draw_screen(scr, heroz->font->gfxfont, frame++, &heroz->screens[screen]);
    dest.x = -1 + 16 * crsrx;
    dest.y = -1 + 16 * crsry;
    src.x = ((SDL_GetTicks() / 387) % 3) * 18;
    src.y = 0;
    src.w = 18;
    src.h = 18;
    SDL_BlitSurface(cursor18, &src, scr, &dest);
    sprintf(buf, "tbank:  %d", tilebank);
    assert(heroz->font->txtfont);
    assert(heroz->font->gfxfont);
    heroz->font->print_at(41 * 16, 16, buf);
    sprintf(buf, "scr: %d", screen);
    heroz->font->print_at(41 * 16, 32, buf);
    sprintf(buf, "X: %d", crsrx);
    heroz->font->print_at(41 * 16, 48, buf);
    sprintf(buf, "Y: %d", crsry);
    heroz->font->print_at(41 * 16, 64, buf);
    i = heroz->screens[screen].s[crsry][crsrx];
    sprintf(buf, "c: %3d '%c' tb: %d ch: %c", i, i, i >> 5, (i & 0x1f) + '@');
    heroz->font->print_at(8, 21 * 16, buf);
    for(i = 0; i < 32; ++i) {
      heroz->font->gfx_at(i << 4, heroz->display->h - 16, i | tilebank << 5);
      heroz->font->print_at(0, heroz->display->h - 32, keychars);
    }
    SDL_Flip(scr);
    SDL_Delay(50);
  } while(!quit);
  return 0;
}

int mythread(void *ptr) {
  Input *input;
  SDLKey key;

  input = heroz->input;
  while(!quit) {
    input->poll_events();
    if(input->is_state(ISS_right)) crsrx = (crsrx + 1) % SCREENWIDTH;
    if(input->is_state(ISS_left)) crsrx = crsrx - 1 < 0 ? SCREENWIDTH - 1 : crsrx - 1;
    if(input->is_state(ISS_down)) crsry = (crsry + 1) % SCREENHEIGHT;
    if(input->is_state(ISS_up)) crsry = crsry - 1 < 0 ? SCREENHEIGHT - 1 : crsry - 1;
    if(input->is_state(ISS_function)) {
      int funckey = input->get_function_key();
      if(funckey <= 8) tilebank = funckey - 1;
    }
    if(input->is_state(ISS_mousebutton)) {
      int x = input->mouse().x;
      int y = input->mouse().y;
      x >>= 4;
      y >>= 4;
      if(x < SCREENWIDTH) crsrx = x;
      if(y < SCREENHEIGHT) crsry = y;
    }
    if(key = input->last_key()) {
      switch(key) {
      case SDLK_KP6:
	heroz->screens.at(screen).shift_right();
	break;
      case SDLK_KP8:
	heroz->screens.at(screen).shift_up();
	break;
      case SDLK_KP4:
	heroz->screens.at(screen).shift_left();
	break;
      case SDLK_KP2:
	heroz->screens.at(screen).shift_down();
	break;
      case SDLK_PAGEDOWN:
	screen = (screen + 1) % heroz->screens.size();
	break;
      case SDLK_PAGEUP:
	if(--screen < 0) screen = heroz->screens.size() - 1;
	break;
      case SDLK_BACKSPACE:
	heroz->screens.at(screen).set(crsrx, crsry, ' ');
	break;
      case SDLK_AT:
      case SDLK_0:
	heroz->screens.at(screen).set(crsrx, crsry, 0 | (tilebank << 5));
	break;
      default:
	if(key >= SDLK_a && key <= SDLK_z)
	  heroz->screens.at(screen).set(crsrx, crsry, (key - SDLK_a + 1) | (tilebank << 5));
	if(key >= SDLK_1 && key <= SDLK_5)
	  heroz->screens.at(screen).set(crsrx, crsry, (key - SDLK_1 + 27) | (tilebank << 5));
      }
    }
    SDL_Delay(87);
  }
  return -42;
}

void callback_quit(Fl_Widget *w, void *data) {
  quit = 1;
}

void callback_clear(Fl_Widget *w, void *data) {
  ScreenCharacters &scr = heroz->screens.at(screen);
  //memset(&scr.s, ' ', sizeof(scr.s));
  scr.clear();
}

void callback_new(Fl_Widget *w, void *data) {
  screen = new_screen(heroz->screens).size() - 1;
}

void callback_save(Fl_Widget *w, void *data) {
  save_screens("data", heroz->screens);
  fl_alert("Screens have been saved!");
}

void callback_copy(Fl_Widget *w, void *data) {
  copy_and_paste_buffer = heroz->screens.at(screen);
}

void callback_paste(Fl_Widget *w, void *data) {
  heroz->screens.at(screen) = copy_and_paste_buffer;
}

int fltkthread(void *null) {
  int pos = 10 - 25;
  CLIArgs *args = static_cast<CLIArgs *>(null);

  Fl_Window *window = new Fl_Window(320, 200);
  Fl_Button *box = new Fl_Button(20, pos += 25, 100, 20, "Quit!");
  box->box(FL_UP_BOX);
  box->labelsize(16);
  box->callback(&callback_quit, NULL);
  // box->labeltype(FL_SHADOW_LABEL);
  box = new Fl_Button(20, pos += 25, 100, 20, "Clear");
  box->callback(&callback_clear, NULL);
  box = new Fl_Button(20, pos += 25, 100, 20, "New");
  box->callback(&callback_new, NULL);
  box = new Fl_Button(20, pos += 25, 100, 20, "Save");
  box->callback(&callback_save, NULL);
  pos = 10 - 25;
  box = new Fl_Button(140, pos += 25, 100, 20, "Copy");
  box->callback(&callback_copy, NULL);
  box = new Fl_Button(140, pos += 25, 100, 20, "Paste");
  box->callback(&callback_paste, NULL);
  window->end();
  window->show(args->argc, args->argv);
  pos = Fl::run();
  printf("fltkthread ret = %d\n", pos);
  quit = 1;
  return pos;
}

int main(int argc, char **argv) {
  SDL_Thread *thread;
  std::vector<SDL_Thread *> threads;
  CLIArgs args = { argc, argv };

  assert(SDL_Init(SDL_INIT_VIDEO) == 0);
  if(!(heroz = new HeroZ)) throw std::runtime_error("new HeroZ");
  heroz->display = SDL_SetVideoMode(800, 480, 32, SDL_HWSURFACE|SDL_ANYFORMAT|SDL_DOUBLEBUF);
  assert(heroz->display);
  SDL_WM_SetCaption("HeroZ Screen Editor", "screen editor");
  SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE); //Ignore all mouse movements...  
  if(!(cursor18 = IMG_ReadXPMFromArray(const_cast<char **>(cursor_18_xpm)))) throw std::runtime_error("no cursor18");
  if(!(heroz->font = new HFont("data", heroz->display))) throw std::runtime_error("new HFont");
  if(!(heroz->input = new Input())) throw std::runtime_error("new Input");
  heroz->screens = load_screens("data");
  assert(heroz->screens.size() > 0);
  
  if(!(thread = SDL_CreateThread(mythread, heroz->display)))
    throw std::runtime_error("mythread");
  else 
    threads.push_back(thread);
  if(!(thread = SDL_CreateThread(displaythread, heroz->display)))
    throw std::runtime_error("displaythread");
  else 
    threads.push_back(thread);
  if(!(thread = SDL_CreateThread(fltkthread, &args)))
    throw std::runtime_error("fltkthread");
  else 
    threads.push_back(thread);

  while(!quit) {
    SDL_Delay(483);
  }
  for(auto i : threads) SDL_KillThread(i);
  SDL_Quit();
  return 0;
}
